package com.symone.crawler;

import cn.hutool.core.date.DateUtil;
import cn.hutool.db.Db;
import cn.hutool.db.Entity;
import com.symone.crawler.config.AirBnbConfig;
import com.symone.crawler.config.CrawlerConfig;
import com.symone.crawler.data.ProxyData;
import com.symone.crawler.log.Log;
import com.symone.crawler.log.LogFactory;
import com.symone.crawler.runnable.*;
import com.symone.crawler.service.BookingService;
import com.symone.crawler.service.ProxyService;

import java.sql.SQLException;
import java.util.*;
import java.util.concurrent.*;

/**
 * Hello world!
 *
 */
public class App {
    private static Log logger = LogFactory.getLog(App.class);

    public static LinkedBlockingQueue<ProxyData> PROXY_DATA_QUEUE = new LinkedBlockingQueue<>();
    public static ConcurrentMap<Long,ProxyData> PROXY_THREAD_MAP = new ConcurrentHashMap<>();
    public static LinkedBlockingQueue<Entity> ROOM_DATA_QUEUE = new LinkedBlockingQueue<>();

    public static void main( String[] args ) throws InterruptedException ,SQLException{
        //ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
        //service.scheduleAtFixedRate(runnable, 10, 1, TimeUnit.SECONDS);

        //startFetchRoomIds(true);
        //startFetchRoomInfos(true);
        //startFetchBooking(true);
        //startCheckRoom(true);

        startFetchBooking(true);

    }


    private static void startCheckRoom(boolean useProxy) throws SQLException {
        logger.info("多次获取数据出错的房间......");
        //SELECT * from airbnb_room where retry_num > 0
        List<Entity> rooms = Db.use().query("SELECT * from airbnb_room where retry_num > 0");
        ExecutorService singleThreadExecutor = null;
        if(rooms.size() > 0) {
            if (useProxy) {
                singleThreadExecutor = Executors.newSingleThreadExecutor();
                Runnable runnable = new ProxyThread();
                singleThreadExecutor.execute(runnable);
            }
        }

        ExecutorService exec = Executors.newFixedThreadPool(useProxy ? CrawlerConfig.THREAD_NUM : 1);
        Iterator<Entity> roomIterator = rooms.iterator();
        while (roomIterator.hasNext()) {
            Entity room = roomIterator.next();
            exec.execute(new CheckThread(room,useProxy));
        }
        exec.shutdown();
        if (useProxy && singleThreadExecutor != null) singleThreadExecutor.shutdown();
    }

    private static void startFetchRoomIds(boolean useProxy) throws InterruptedException {
        logger.info("开始查询地区范围的所有房间......");
        ExecutorService singleThreadExecutor = null;
        if (useProxy) {
            singleThreadExecutor = Executors.newSingleThreadExecutor();
            Runnable runnable = new ProxyThread();
            singleThreadExecutor.execute(runnable);
        }

        ExecutorService exec = Executors.newFixedThreadPool(useProxy ? CrawlerConfig.THREAD_NUM : 1);
        int curPrice = AirBnbConfig.base_price;
        while (curPrice < 5000){
            int curPage = 1;
            int addPrice = (int)(Math.random()*10);
            while (curPage < AirBnbConfig.search_max_page ){
                exec.execute(new SearchThread(curPage,curPrice,addPrice,useProxy));
                curPage ++;
            }
            curPrice += addPrice;
        }

        exec.shutdown();
        if (useProxy && singleThreadExecutor != null) singleThreadExecutor.shutdown();
        //logger.info("查询地区范围的所有房间完毕!");
    }

    private static void startFetchRoomInfos(boolean useProxy) throws InterruptedException {
        logger.info("开始补全房屋信息......");
        ExecutorService singleThreadExecutor = null;
        if(getFixingRoomIdList()) {
            if (useProxy) {
                singleThreadExecutor = Executors.newSingleThreadExecutor();
                Runnable runnable = new ProxyThread();
                singleThreadExecutor.execute(runnable);
            }
        }

        ExecutorService exec = Executors.newFixedThreadPool(useProxy ? CrawlerConfig.THREAD_NUM : 1);
        while (!ROOM_DATA_QUEUE.isEmpty()) {
            Entity room = ROOM_DATA_QUEUE.take();
            exec.execute(new InfoThread(room,useProxy));
        }
        exec.shutdown();
        if (useProxy && singleThreadExecutor != null) singleThreadExecutor.shutdown();
        //logger.info("补全房屋信息完毕!");
    }

    private static void startFetchBooking(boolean useProxy) throws InterruptedException {
        logger.info("更新房屋预定 Booking 信息......");
        if(checkAndCreateBookingTable()) {
            ExecutorService singleThreadExecutor = null;
            if (fetchAllEntities()) {
                if (useProxy) {
                    singleThreadExecutor = Executors.newSingleThreadExecutor();
                    Runnable runnable = new ProxyThread();
                    singleThreadExecutor.execute(runnable);
                }
            }

            ExecutorService exec = Executors.newFixedThreadPool(useProxy ? CrawlerConfig.THREAD_NUM : 1);
            while (!ROOM_DATA_QUEUE.isEmpty()) {
                Entity room = ROOM_DATA_QUEUE.take();
                exec.execute(new BookingThread(room, useProxy));
            }

            exec.shutdown();
            if (useProxy && singleThreadExecutor != null) singleThreadExecutor.shutdown();
        }
        //logger.info("房屋预定信息更新完毕!");
    }

    private static boolean fetchAllEntities() {
        if(!ROOM_DATA_QUEUE.isEmpty()) ROOM_DATA_QUEUE.clear();
        //select * from airbnb_room where id in(select room_id from airbnb_room_booking where date = '2019-05-27' AND (update_time is null OR update_time < DATE_FORMAT('2019-05-27 00:00:00','%Y-%m-%d %H:%i:%S')));
        String tableName = "airbnb_room_booking_" + CrawlerConfig.DATABASE_MONTH_FORMATTER.format(new Date());
       try {
            String now = CrawlerConfig.DATE_FORMATTER.format(new Date());
//           List<Entity> rooms = Db.use().query("select * from airbnb_room where is_cancel = 0 AND retry_num <= "
//                   + (CrawlerConfig.MAX_RETRY * 5) + " AND ( id in(select room_id from " + tableName +" where date = '"
//                   + now + "' AND (update_time is null OR update_time < DATE_FORMAT('" + now + " 00:00:00','%Y-%m-%d %H:%i:%S'))) OR id not IN (select room_id from " + tableName + "))");
            //SELECT * FROM airbnb_room WHERE is_cancel = 0 AND (check_code = 0 OR check_code = 200) AND id NOT IN (SELECT room_id FROM airbnb_room_booking_2019_06 )
           //List<Entity> rooms = Db.use().query("SELECT * FROM airbnb_room WHERE is_cancel = 0 AND (check_code = 0 OR check_code = 200)  AND id NOT IN (SELECT room_id FROM airbnb_room_booking_2019_06 WHERE date='2019-06-08' )  OR id NOT IN (SELECT room_id FROM airbnb_room_booking_2019_05);");
            List<Entity> rooms = Db.use().query("SELECT * FROM airbnb_room WHERE is_cancel = 0 AND (check_code = 0 OR check_code = 200)");
            //List<Entity> rooms = Db.use().findAll("airbnb_room");
            Iterator<Entity> roomIterator = rooms.iterator();
            while (roomIterator.hasNext()) {
                Entity room = roomIterator.next();
                ROOM_DATA_QUEUE.add(room);
            }
//            int allRowNum =  Db.use().count(new Entity("airbnb_room"));
//            CrawlerConfig.THREAD_NUM = (rooms.size() / allRowNum) * 100;
//            CrawlerConfig.PROXY_NUM = (rooms.size() / allRowNum) * 100;
            return rooms.size() > 0;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return false;
    }

    private static boolean getFixingRoomIdList(){
        logger.info("更新补全房屋信息......");
        if(!ROOM_DATA_QUEUE.isEmpty()) ROOM_DATA_QUEUE.clear();
        try {
            List<Entity> rooms = Db.use().findAll(Entity.create("airbnb_room").set("price" , "is null"));
            Iterator<Entity> roomIterator = rooms.iterator();
            while (roomIterator.hasNext()) {
                Entity room = roomIterator.next();
                logger.info("Fetch Room ID:" + room.getStr("id") + " -[" + room.getStr("name") +"] data ... ...");
                ROOM_DATA_QUEUE.add(room);
            }
            return rooms.size() > 0;
        }catch (Exception ex){
            ex.printStackTrace();
            logger.error(ex.getMessage());
        }
        return false;
    }

    private static boolean checkAndCreateBookingTable(){
        String tableName = "airbnb_room_booking_" + CrawlerConfig.DATABASE_MONTH_FORMATTER.format(new Date());
        String checkSql = "show tables like '" + tableName + "'";
        String createSql = "CREATE TABLE `" + tableName + "` ("
                + "  `room_id` bigint(20) NOT NULL,"
                + "  `date` varchar(20) NOT NULL,"
                + "  `native_adjusted_price` decimal(12,2) DEFAULT NULL,"
                + "  `native_currency` varchar(16) NOT NULL,"
                + "  `native_price` decimal(12,2) NOT NULL,"
                + "  `type` varchar(32) DEFAULT NULL,"
                + "  `local_price_formatted` varchar(64) DEFAULT NULL,"
                + "  `is_price_upon_request` varchar(128) DEFAULT NULL,"
                + "  `is_used` tinyint(1) NOT NULL,"
                + "  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,"
                + "  `update_time` datetime DEFAULT NULL,"
                + "  PRIMARY KEY (`room_id`,`date`),"
                + "  KEY `" + tableName + "_room_id_date_index` (`room_id`,`date`) USING BTREE,"
                + "  KEY `" + tableName + "_room_id_index` (`room_id`) USING BTREE,"
                + "  KEY `" + tableName + "_date_index` (`date`) USING BTREE"
                + ") ENGINE=MyISAM DEFAULT CHARSET=utf8mb4";

        try {
            List<Entity> checkResult = Db.use().query(checkSql);
            if(checkResult.size() == 0){
                Db.use().execute(createSql);
            }
        } catch (SQLException e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

}
